﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Drill.TestSupport.Mocks.MockDrillBits;
using NUnit.Framework;

namespace Drill.Core
{
	[TestFixture]
	public class DependencyResolverManagementDictionaryTests
	{
		[Test]
		public void CanConstruct()
		{
			// Arrange
			Dictionary<string, IDependencyResolver> resolvers = 
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", new SimpleDomainMockDrillBit() }
					  , { "qwerty", new AutomobileDomainMockDrillBit() }
					};

			// Act
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Assert
			Assert.That(managementDictionary, Is.Not.Null);
		}

		[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "resolvers", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotConstructWithNullResolversArgument()
		{
			// Arrange
			Dictionary<string, IDependencyResolver> resolvers = null;

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			// ReSharper disable ObjectCreationAsStatement
			new DependencyResolverManagementDictionary(resolvers);
			// ReSharper restore ObjectCreationAsStatement
			// ReSharper restore ExpressionIsAlwaysNull

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Test]
		public void CanInvokeContainsKeyWithValidKey()
		{
			// Arrange
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", new SimpleDomainMockDrillBit() }
					  , { "qwerty", new AutomobileDomainMockDrillBit() }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			bool containsKey = managementDictionary.ContainsKey("asdf");

			// Assert
			Assert.That(containsKey, Is.True);
		}

		[Test]
		public void CanInvokeContainsKeyWithInvalidKey()
		{
			// Arrange
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", new SimpleDomainMockDrillBit() }
					  , { "qwerty", new AutomobileDomainMockDrillBit() }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			bool containsKey = managementDictionary.ContainsKey("hello");

			// Assert
			Assert.That(containsKey, Is.False);
		}

		[Test]
		public void CanInvokeTryGetValueWithValidKey()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			IDependencyResolver resolver;
			bool success = managementDictionary.TryGetValue("qwerty", out resolver);

			// Assert
			Assert.That(success, Is.True);
			Assert.That(resolver, Is.SameAs(resolver2));

			// Act
			success = managementDictionary.TryGetValue("asdf", out resolver);

			// Assert
			Assert.That(success, Is.True);
			Assert.That(resolver, Is.SameAs(resolver1));
		}

		[Test]
		public void CanInvokeTryGetValueWithInvalidKey()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			IDependencyResolver resolver;
			bool success = managementDictionary.TryGetValue("hello", out resolver);

			// Assert
			Assert.That(success, Is.False);
			Assert.That(resolver, Is.Null);
		}

		[Test]
		public void CanInvokeIndexerWithValidKey()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			IDependencyResolver resolver = managementDictionary["qwerty"];

			// Assert
			Assert.That(resolver, Is.SameAs(resolver2));

			// Act
			resolver = managementDictionary["asdf"];

			// Assert
			Assert.That(resolver, Is.SameAs(resolver1));
		}

		[ExpectedException(typeof(KeyNotFoundException), ExpectedMessage = "The given key was not present in the dictionary", MatchType = MessageMatch.Contains)]
		[Test]
		public void CanInvokeIndexerWithInvalidKey()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			// ReSharper disable UnusedVariable
			IDependencyResolver resolver = managementDictionary["hello"];
			// ReSharper restore UnusedVariable

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Test]
		public void CanGetKeysProperty()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			IEnumerable<string> keys = managementDictionary.Keys;
			List<string> list = keys.ToList();

			// Assert
			Assert.That(keys, Is.Not.Null);
			Assert.That(list.Count, Is.EqualTo(2));
			Assert.That(list[0], Is.EqualTo("asdf"));
			Assert.That(list[1], Is.EqualTo("qwerty"));
		}

		[Test]
		public void CanGetValuesProperty()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			IEnumerable<IDependencyResolver> values = managementDictionary.Values;
			List<IDependencyResolver> list = values.ToList();

			// Assert
			Assert.That(values, Is.Not.Null);
			Assert.That(list.Count, Is.EqualTo(2));
			Assert.That(list[0], Is.SameAs(resolver1));
			Assert.That(list[1], Is.SameAs(resolver2));
		}

		[Test]
		public void CanGetEnumerator()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			IEnumerable enumerable = managementDictionary;
			IEnumerator enumerator = enumerable.GetEnumerator();

			// Assert
			Assert.That(enumerator, Is.Not.Null);

			int count = 0;
			while(enumerator.MoveNext())
			{
				count++;
				object item = enumerator.Current;
				KeyValuePair<string, IDependencyResolver> kvp = (KeyValuePair<string, IDependencyResolver>) item;
				Assert.That(kvp, Is.Not.Null);
				switch (count)
				{
					case 1:
						Assert.That(kvp.Key, Is.EqualTo("asdf"));
						Assert.That(kvp.Value, Is.SameAs(resolver1));
						break;

					case 2:
						Assert.That(kvp.Key, Is.EqualTo("qwerty"));
						Assert.That(kvp.Value, Is.SameAs(resolver2));
						break;

					default:
						Assert.Fail("There must only be 2 items enumerated by the enumerator.");
						break;
				}
			}
		}

		[Test]
		public void CanGetEnumeratorGeneric()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			IEnumerator<KeyValuePair<string, IDependencyResolver>> enumerator = managementDictionary.GetEnumerator();

			// Assert
			Assert.That(enumerator, Is.Not.Null);

			int count = 0;
			while (enumerator.MoveNext())
			{
				count++;
				KeyValuePair<string, IDependencyResolver> kvp = enumerator.Current;
				Assert.That(kvp, Is.Not.Null);
				switch (count)
				{
					case 1:
						Assert.That(kvp.Key, Is.EqualTo("asdf"));
						Assert.That(kvp.Value, Is.SameAs(resolver1));
						break;

					case 2:
						Assert.That(kvp.Key, Is.EqualTo("qwerty"));
						Assert.That(kvp.Value, Is.SameAs(resolver2));
						break;

					default:
						Assert.Fail("There must only be 2 items enumerated by the enumerator.");
						break;
				}
			}
		}

		[Test]
		public void CanGetCountProperty()
		{
			// Arrange
			IDependencyResolver resolver1 = new SimpleDomainMockDrillBit();
			IDependencyResolver resolver2 = new AutomobileDomainMockDrillBit();
			Dictionary<string, IDependencyResolver> resolvers =
				new Dictionary<string, IDependencyResolver>
					{
						{ "asdf", resolver1 }
					  , { "qwerty", resolver2 }
					};
			DependencyResolverManagementDictionary managementDictionary =
				new DependencyResolverManagementDictionary(resolvers);

			// Act
			int count = managementDictionary.Count;

			// Assert
			Assert.That(count, Is.EqualTo(2));
		}
	}
}